package org.dromara.system.controller.system;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONUtil;
import lombok.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.domain.R;
import org.dromara.common.web.core.BaseController;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.Serializable;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 地区接口
 *
 * @author zlj
 * @date 2024-05-08
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/area")
public class SysAreaController extends BaseController {

    /**
     * 查询地区列表
     */
    @SaIgnore
    @Cacheable(cacheNames = CacheNames.SYS_AREA)
    @GetMapping("/list")
    public R<List<AreaTree>> list() {
        List<String> lines = FileUtil.readUtf8Lines(Objects.requireNonNull(SysAreaController.class.getResource("/china-area.json")));
        String file = String.join("", lines);
        List<AreaTree> areas = JSONUtil.toList(file, AreaTree.class);
        Iterator<AreaTree> iterator = areas.iterator();
        while (iterator.hasNext()) {
            AreaTree next = iterator.next();
            if (AreaEnum.CITY.getCode().equals(next.getLevel()) || AreaEnum.AREA.getCode().equals(next.getLevel())) {
                if (StringUtils.length(next.getName()) < 2) {
                    iterator.remove();
                }
                if (StringUtils.equals(next.getName(), "市辖区")) {
                    iterator.remove();
                }
            }
        }
        areas = TreeUtils.buildPath(areas, o -> Objects.equals(o.getParentCode(), 0L));
        Map<Integer, List<AreaTree>> areaMapping = areas.stream().collect(Collectors.groupingBy(AreaTree::getLevel));
        return R.ok(areaMapping.get(AreaEnum.PROVINCE.getCode()));
    }

    /**
     * 中国行政地区
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString(exclude = {"parent", "children"})
    public static class AreaTree implements ITree<AreaTree, Long> {
        private static final long serialVersionUID = -32407026969579150L;
        /**
         * 层级
         */
        private Integer level;
        /**
         * 父级行政代码
         */
        private Long parentCode;
        /**
         * 行政代码
         */
        private Long areaCode;
        /**
         * 名称
         */
        private String name;

        /**
         * 子节点
         */
        private List<AreaTree> children;

        @Override
        public Long id() {
            return areaCode;
        }

        @Override
        public Long parentId() {
            return this.parentCode;
        }

        @Override
        public void children(List<AreaTree> children) {
            this.children = children;
        }

        public List<AreaTree> getChildren() {
            return Objects.isNull(this.children) ? null : this.children;
        }
    }

    /**
     * 树接口
     */
    public interface ITree<T, K> {


        /**
         * 节点ID
         *
         * @return
         */
        K id();


        /**
         * 父节点ID
         *
         * @return
         */
        K parentId();

        /**
         * 设置父节点
         *
         * @param parent
         */
        default void parent(T parent) {
        }

        /**
         * 用于拼接 path 的属性
         * <p>
         * child.path(parentNode.path() + spliterator + child.pathProperty());
         *
         * @return
         */
        default String pathProperty() {
            return StringUtils.EMPTY;
        }

        /**
         * 获取节点名称
         *
         * @return
         */
        default String path() {
            return StringUtils.EMPTY;
        }

        /**
         * 设置节点名称
         *
         * @param path
         */
        default void path(String path) {
        }

        /**
         * 排序
         *
         * @return
         */
        default Integer index() {
            return 0;
        }

        /**
         * 设置子节点
         *
         * @param children
         */
        default void children(List<T> children) {
        }
    }

    public enum AreaEnum {
        PROVINCE(0, "省/直辖市"),
        CITY(1, "市/州"),
        AREA(2, "县/区 "),
        TOWN(3, "乡/镇"),
        VILLAGE(4, "村/社区");

        private final int code;

        private final String message;


        AreaEnum(int code, String message) {
            this.code = code;
            this.message = message;
        }

        public Integer getCode() {
            return this.code;
        }

        public String getMessage() {
            return this.message;
        }
    }

    /**
     * 将扁平数据构造成树结构
     *
     * @author: Neo
     * @date: 2021/1/4 16:49
     * @version: 1.0
     */
    public class TreeUtils {

        public static final String DEFAULT_SPLITERATOR = "/";

        private TreeUtils() {
        }


        enum Model {
            TREE, PATH, TREE_AND_PATH
        }


        /**
         * 构造树的入口方法
         *
         * @param originData    数据列表
         * @param rootPredicate 根节点条件
         * @param <T>           implements ITree
         * @param <K>           implements Serializable
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> buildTree(List<T> originData, Predicate<T> rootPredicate) {
            return baseBuild(Model.TREE, originData, null, rootPredicate);
        }


        /**
         * 构建树路径，不改变数据结构
         *
         * @param originData
         * @param rootPredicate
         * @param <T>
         * @param <K>
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> buildPath(List<T> originData, Predicate<T> rootPredicate) {
            return buildPath(originData, DEFAULT_SPLITERATOR, rootPredicate);
        }

        /**
         * 构建树路径，不改变数据结构
         *
         * @param originData
         * @param spliterator
         * @param rootPredicate
         * @param <T>
         * @param <K>
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> buildPath(List<T> originData, CharSequence spliterator, Predicate<T> rootPredicate) {
            return baseBuild(Model.PATH, originData, spliterator, rootPredicate);
        }

        /**
         * 构建树和路径
         *
         * @param originData
         * @param rootPredicate
         * @param <T>
         * @param <K>
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> buildTreeAndPath(List<T> originData, Predicate<T> rootPredicate) {
            return buildTreeAndPath(originData, DEFAULT_SPLITERATOR, rootPredicate);
        }

        /**
         * 构建树和路径
         *
         * @param originData
         * @param spliterator
         * @param rootPredicate
         * @param <T>
         * @param <K>
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> buildTreeAndPath(List<T> originData, CharSequence spliterator, Predicate<T> rootPredicate) {
            return baseBuild(Model.TREE_AND_PATH, originData, spliterator, rootPredicate);
        }


        /**
         * 树构建的基础方法
         *
         * @param model
         * @param originData
         * @param spliterator
         * @param rootPredicate
         * @param <T>
         * @param <K>
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> baseBuild(Model model,
                                                                                        List<T> originData,
                                                                                        CharSequence spliterator,
                                                                                        Predicate<T> rootPredicate) {
            if (Objects.isNull(model) || CollectionUtils.isEmpty(originData) || Objects.isNull(rootPredicate)) {
                return Collections.EMPTY_LIST;
            }

            List<T> result = new ArrayList<>(CollectionUtils.size(originData));

            List<T> roots = originData.stream().filter(rootPredicate).sorted(Comparator.comparing(T::index)).collect(Collectors.toList());

            if (CollectionUtils.isEmpty(roots)) {
                return Collections.EMPTY_LIST;
            }

            // 删除根节点，避免重复遍历
            originData.removeAll(roots);

            switch (model) {
                case TREE:
                    roots.forEach(r -> result.add(buildTree(r, originData)));
                    break;
                case PATH:
                    roots.forEach(r -> result.addAll(buildPath(r, originData, spliterator)));
                    break;
                case TREE_AND_PATH:
                    roots.forEach(r -> result.add(buildTreeAndPath(r, originData, spliterator)));
                    break;
                default:
                    throw new RuntimeException();
            }
            return result;
        }


        /**
         * 构建子节点
         *
         * @param parentNode 父节点
         * @param originData 数据列表
         * @param <T>        implements ITree
         * @param <K>        implements Serializable
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> T buildTree(T parentNode, List<T> originData) {
            List<T> childrenNode = new ArrayList<>();

            List<T> children = filterByParentId(parentNode.id(), originData);

            if (CollectionUtils.isNotEmpty(children)) {
                // 删除节点，避免重复遍历
                originData.removeAll(children);
            }
            for (T child : children) {
//            child.parent(parentNode);
                childrenNode.add(buildTree(child, originData));
            }
            parentNode.children(childrenNode);
            return parentNode;
        }


        /**
         * 构建子节点路径
         *
         * @param parentNode
         * @param originData
         * @param spliterator
         * @param <T>
         * @param <K>
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> buildPath(T parentNode, List<T> originData, CharSequence spliterator) {
            List<T> result = new ArrayList<>();

            List<T> children = filterByParentId(parentNode.id(), originData);

            result.add(parentNode);

            if (CollectionUtils.isEmpty(children)) {
                return result;
            }

            // 删除节点，避免重复遍历
            originData.removeAll(children);

            for (T child : children) {
//            child.parent(parentNode);
                child.path(parentNode.path() + spliterator + child.pathProperty());
                result.addAll(buildPath(child, originData, spliterator));
            }
            parentNode.children(children);
            return result;
        }

        /**
         * 构建树和路径
         *
         * @param parentNode
         * @param originData
         * @param spliterator
         * @param <T>
         * @param <K>
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> T buildTreeAndPath(T parentNode, List<T> originData, CharSequence spliterator) {
            List<T> children = filterByParentId(parentNode.id(), originData);

            if (CollectionUtils.isNotEmpty(children)) {
                originData.removeAll(children);
            }

            for (T child : children) {
//            child.parent(parentNode);
                child.path(parentNode.path() + spliterator + child.path());
                buildTreeAndPath(child, originData, spliterator);
            }

            parentNode.children(children);
            return parentNode;

        }


        /**
         * @param parentId   父节点ID
         * @param originData 数据列表
         * @param <T>        implements ITree
         * @param <K>        implements Serializable
         * @return
         */
        public static <T extends ITree<T, K>, K extends Serializable> List<T> filterByParentId(K parentId, List<T> originData) {
            return originData.stream()
                .filter(i -> Objects.equals(parentId, i.parentId()))
                .sorted(Comparator.comparing(T::index))
                .collect(Collectors.toList());

        }
    }


}
